export { }

// 条件类型
interface Fish {
  name1: string
}
interface Water {
  name2: string
}
interface Bird {
  name3: string
}
interface Sky {
  name4: string
}

type Condition<T> = T extends Fish ? Water : Sky
let con: Condition<Fish> = {
  name2: '水'
}
// 条件类型的分发
let con1: Condition<Fish | Bird> = { name2: '' }
let con2: Condition<Fish | Bird> = { name4: '' }

// 找出T中不包含U的部分
type Diff<T, U> = T extends U ? never : T
type R = Diff<'a' | 'b' | 'c' | 'd', 'd' | 'e' | 'f'> // type R = "a" | "b" | "c"

type Filter<T, U> = T extends U ? T : never
type R2 = Filter<'a' | 'b' | 'c' | 'd', 'd' | 'e' | 'f'> // type R2 = "d"

// 内置条件类型
// Exclude  排除 从T中排除U
type Exclude<T, U> = T extends U ? never : T
type R3 = Exclude<'a' | 'b' | 'c' | 'd', 'd' | 'e' | 'f'>  // type R3 = "a" | "b" | "c"

// Extract 提取  从T中提取U
type Extract<T, U> = T extends U ? T : never
type R4 = Filter<'a' | 'b' | 'c' | 'd', 'd' | 'e' | 'f'> // type R2 = "d"

// NonNullable 将null和undefined去除
type NonNullable<T> = T extends null | undefined ? never : T;
type R5 = NonNullable<'a' | null | undefined | '123'> // type R5 = "a" | "123"

// ReurnType 获取函数的返回值类型 infer 表示定义一个待确认的类型
type ReurnType<T> = T extends (...args: any[]) => infer R ? R : T
function getUser(a: number, b: string) {
  return { name: 'smd' }
}
type GetUserType = typeof getUser
type ReturnUserFn = ReturnType<GetUserType> // type ReturnUserFn = { name: string; }

// Parameters 获取函数参数 返回元组类型
// type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
type ParamType = Parameters<GetUserType> // type ParamType = [a: number, b: string]

// InstanceType 获取构造函数 实例的类型
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;
class Person {
  name: string
  constructor(name: string) {
    this.name = name
  }
  getName() {
    return this.name
  }
}
type PersonInstance = InstanceType<typeof Person> // type PersonInstance = Person
// ConstructorParameters 获取构造函数 实例参数的类型  返回元组类型
type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never;
type parmas = ConstructorParameters<typeof Person>  // type parmas = [name: string]

// infer 应用案例
// tuple 转 union 元组转联合类型
type Ttuple = [string, number]
type TtupleToUnion = ElementOf<Ttuple> // string|number 
type ElementOf<T> = T extends Array<infer E> ? E : never

namespace a {
  // 参数=> 返回值   参数:返回值   联合类型转交叉类型
  type T1 = { name: string }
  type T2 = { age: number }
  type ToIntersection<T> = T extends { a: (x: infer U) => void, b: (x: infer U) => void } ? U : never  // infer U 既修饰第一个参数又修饰第二个参数 所以得到的U应该是T1和T2的交叉类型
  type T3 = ToIntersection<{ a: (x: T1) => void, b: (x: T2) => void }>
}

namespace a {

  // 1. Partial 将类型变为可选的
  interface a {
    a: string
    b: number
    c: boolean
  }

  type Partial<T> = {
    [P in keyof T]?: T[P];
  };

  type PartialA = Partial<a>
  /**
    type PartialA = {
        a?: string | undefined;
        b?: number | undefined;
        c?: boolean | undefined;
    }
   */
  let a: PartialA = {
    a: ''
  }

  // 将所有属性都变为可选的
  interface Company {
    id: number
    name: string
  }
  interface Person {
    id: number
    name: string
    company: Company
  }

  type DeepPartial<T> = {
    // 拿到T类型中的每个key U   如果T[U]类型 是对象  就递归  否则返回 T[U]
    [U in keyof T]?: T[U] extends object ? DeepPartial<T[U]> : T[U]
  }
  type PartialPerson = DeepPartial<Person>
  let p: PartialPerson = {
    id: 18,
    company: {
      id: 18
    }
  }

  // 2. Required 将可选项变为必填项
  interface Person1 {
    name: string
    age?: number
  }

  type Required<T> = {
    [P in keyof T]-?: T[P]   // -? 不可选
  }
  type InRequired = Required<Person1>
  let p1: InRequired = {
    name: 'smd',
    age: 18 // 不写age会有类型校验错误
  }

  // 3. Readonly 将所有属性变为只读的
  interface Person2 {
    name: string
    age?: number
  }
  type Readonly<T> = {
    readonly [P in keyof T]: T[P]
  }
  type ReadonlyPerson = Readonly<Person2>
  let p2: ReadonlyPerson = {
    name: 'smd',
    age: 18
  }
  // 错误 p2.age = 20
  // 错误 p2.name = 'sg' // 无法分配到 "name" ，因为它是只读属性。

  // 4. Pick 从一个类型中拾取指定属性
  interface Person3 {
    name: string
    age: number
    gender: number
  }

  let p3: Person3 = { name: 'smd', age: 18, gender: 2 };
  type KeyofPerson3 = keyof Person3;

  type Pick<T, K extends keyof T> = { // K extends keyof T 表示K是 T的子类型
    [P in K]: T[P];
  }
  type PickPerson = Pick<Person3, 'name' | 'age'>

  // 5. Extract<T,U> 提取  从T 中提取U

  type Ex = Extract<number | string | boolean, string | boolean>
  let ex: Ex = true // type Ex = string | boolean

  // 6. Record  将一个类型的所有属性值都映射到另一个类型上并创造一个新的类型
  function mapObj<K extends string | number, T, U>(obj: Record<K, T>, map: (x: T) => U) {
    let rest: Record<K, U> = <Record<K, U>>{}
    for (const key in obj) {
      rest[key] = map(obj[key])
    }
    return rest
  }
  let obj = { count1: 1, count2: 2, 3: 3 }
  let map = (x: number): string => x * 2 + ''
  let newObj = mapObj<string | number, number, string>(obj, map)
  console.log(newObj) // { '3': '6', count1: '2', count2: '4' }
}

